from datetime import datetime
from functools import reduce

from flask import session
from sqlalchemy import or_
from werkzeug.security import generate_password_hash, check_password_hash
from app import db


class Menu(db.Model):
    """
    菜单
    """
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255), unique=True)
    icon = db.Column(db.String(255), nullable=True)
    permissions = db.relationship("Permission", backref="permission_menu", lazy="dynamic") # Do not use 'permission', it must be a class not table_name

    def _to_dict(self):
        pass


roles_permissions = db.Table(
    'roles_permissions',
    db.Column('role_id', db.Integer, db.ForeignKey('role.id')),
    db.Column('permission_id', db.Integer, db.ForeignKey('permission.id'))
)

roles_users = db.Table(
    'roles_users',
    db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
    db.Column('role_id', db.Integer, db.ForeignKey('role.id'))
)


class Permission(db.Model):
    __tablename__ = "permission"

    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(64))
    url = db.Column(db.String(255))  # 菜单路径
    parent_id = db.Column(db.Integer)
    permission_desc = db.Column(db.String(128), unique=True)
    is_menu = db.Column(db.Boolean, default=False, comment="是否为父级菜单")
    menu = db.Column(db.Integer, db.ForeignKey('menu.id'))

    def to_dict(self):
        return {
            'id': self.id,
            'title': self.title,
            'url': self.url,
            'is_menu': self.is_menu
        }


class Role(db.Model):
    __tablename__ = "role"

    id = db.Column(db.Integer, primary_key=True)
    role_name = db.Column(db.String(32), unique=True, comment="角色名称")
    users = db.relationship('User', secondary=roles_users, backref=db.backref('roles', lazy='dynamic'))
    permissions = db.relationship('Permission', secondary=roles_permissions, backref=db.backref('roles', lazy='dynamic'))

    def add_permission(self, perm):
        pass

    def add_permissions(self):
        self.add_permission()

    def to_dict(self):
        return {
            'id': self.id,
            'role_name': self.role_name,
            # 'permission': self.permission
        }


class User(db.Model):
    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(48), unique=True, nullable=False)
    _password_hash = db.Column(db.String(128), nullable=False)
    email = db.Column(db.String(64))
    # avatar = db.Column(db.String(128), comment="头像")
    # is_active = db.Column(db.Boolean, default=False)
    # is_superuser = db.Column(db.Boolean, default=False)
    # last_login = db.Column(db.DateTime, default=datetime.now)
    # register_time = db.Column(db.DateTime, default=datetime.now)

    # roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic'))

    @property
    def password(self):
        return self._password_hash

    @password.setter
    def password(self, raw):
        self._password_hash = generate_password_hash(raw)

    def verify_password(self, raw):
        """
        校验密码
        :param raw:
        :return: true or false
        """
        return check_password_hash(self._password_hash, raw)

    def __repr__(self):
        return self.name

    def user_perms(self):
        """
        获取用户的所有权限
        :return: ["/user/add", ... ]
        """
        if self.roles is None:
            print('role is none')
            return False
        all_perms = []
        all_perms_obj = list(map(lambda r: r.permissions, self.roles))
        if not all(all_perms_obj):
            return False

        # for perms in all_perms_obj:
        #     all_perms.append(reduce(or_, map(lambda x: x.url, perms)))
        # all_perms = reduce(or_, map(lambda x: x.permissions, self.roles))
        for perms in all_perms_obj:
            all_perms.extend(list(map(lambda x: x.url, perms)))
        all_perms = list(set(all_perms))
        return all_perms

    def has_per(self, permission):
        if self.is_active and self.is_superuser:
            return True
        if self.roles is None:
            print('role is none')
            return False
        all_perms = []
        all_perms_obj = list(map(lambda r: r.permissions, self.roles))
        if not all(all_perms_obj):
            return False

        for perms in all_perms_obj:
            all_perms.append(reduce(or_, map(lambda x: x.url, perms)))
        # all_perms = reduce(or_, map(lambda x: x.permissions, self.roles))
        return permission in all_perms

    @classmethod
    def add_role(cls, user_id, role_id):
        role = Role.query.filter(Role.id == role_id).first()
        user = User.query.filter(User.id == user_id).first()
        user.roles.append(role)
        db.session.add(user)
        db.session.commit()

    @classmethod
    def add_roles(cls, user_id, role_ids):
        roles = Role.query.filter(Role.id.in_(role_ids))
        user = User.query.filter(User.id == user_id).first()
        user.roles = roles
        db.session.add(user)
        db.session.commit()

    def to_dict(self):
        return {
            "id": self.id,
            "name": self.name,
            "email": self.email,
            # "last_login": self.last_login,
        }

    def row2dict(row):
        d = {}
        for column in row.__table__.columns:
            d[column.name] = str(getattr(row, column.name))
        print(d)
        return d
